package me.zhengjie.recitation.util;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.S3Exception;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Vector;

/**
 * SFTP 文件删除工具类，提供文件删除及空目录清理功能
 *
 * @author yellow
 * @since 2025-04-06
 */
@Component
@Slf4j
public class FileDeleteUtils {
    private static final Logger logger = LoggerFactory.getLogger(FileDeleteUtils.class);

    @Value("${config.use-remote-service}")
    private boolean useRemoteService;

    /**
     * 本地存储根路径
     */
    @Value("${local.base-path:/data/uploads-local}")
    private String localBasePath;

    @Value("${remote.base-path}")
    private String basePath;

    @Autowired
    private GenericObjectPool<ChannelSftp> sftpPool; // SFTP 连接池

    @Value("${cloud.aws.s3.bucket}")
    private String bucketName;

    @Autowired
    private S3Client s3Client;

    /**
     * 从 Cloudflare R2 删除指定的对象。
     *
     * @param objectKey 要删除的对象键（与上传时一致）
     * @return 如果对象存在并成功删除返回 true；如果对象不存在返回 false；出现其他错误抛出异常。
     */
    public boolean deleteFromR2(String objectKey) {
        // 1. 参数校验
        if (!StringUtils.hasText(bucketName)) {
            throw new IllegalStateException("Bucket 名称未配置或为空");
        }
        if (!StringUtils.hasText(objectKey)) {
            throw new IllegalArgumentException("objectKey 不能为空");
        }

        if (!useRemoteService) {
            // 本地删除
            Path target = Paths.get(objectKey);
            try {
                if (Files.exists(target)) {
                    Files.delete(target);
                    log.info("本地文件已删除: {}", target);
                    return true;
                } else {
                    log.warn("本地文件不存在，无需删除: {}", target);
                    return false;
                }
            } catch (IOException e) {
                throw new RuntimeException("本地删除失败: " + e.getMessage(), e);
            }
        }

        // 远程 R2 删除
        // 2. 检查对象是否存在
        try {
            HeadObjectRequest headReq = HeadObjectRequest.builder()
                    .bucket(bucketName)
                    .key(objectKey)
                    .build();
            s3Client.headObject(headReq);
        } catch (S3Exception e) {
            // 如果是 404（NoSuchKey/AWS SDK v2 会抛 NoSuchKeyException 或 S3Exception with code 404）
            String code = e instanceof S3Exception ? ((S3Exception)e).statusCode() + "" : "";
            if ("404".equals(code) || e instanceof NoSuchKeyException) {
                log.warn("对象不存在，无需删除: bucket={}, key={}", bucketName, objectKey);
                return false;
            }
            // 其他 S3 异常，继续抛出
            throw new RuntimeException("检查对象存在性失败: " + e.getMessage(), e);
        }

        // 3. 执行删除
        try {
            DeleteObjectRequest delReq = DeleteObjectRequest.builder()
                    .bucket(bucketName)
                    .key(objectKey)
                    .build();
            s3Client.deleteObject(delReq);
            log.info("成功删除对象: bucket={}, key={}", bucketName, objectKey);
            return true;
        } catch (S3Exception e) {
            throw new RuntimeException("删除失败: " + e.awsErrorDetails().errorMessage(), e);
        } catch (SdkException e) {
            throw new RuntimeException("S3 客户端异常: " + e.getMessage(), e);
        }
    }


//    /**
//     * 批量删除对象（可选扩展），最多一次支持 1000 个 key。
//     *
//     * @param keys 要删除的一组 objectKey
//     * @return 删除成功的 key 列表
//     */
//    public List<String> deleteBatchFromR2(List<String> keys) {
//        Objects.requireNonNull(keys, "keys 列表不能为空");
//        if (keys.isEmpty()) {
//            return List.of();
//        }
//
//        // 参数校验
//        if (!StringUtils.hasText(bucketName)) {
//            throw new IllegalStateException("Bucket 名称未配置或为空");
//        }
//
//        // 构造批量删除请求
//        List<ObjectIdentifier> identifiers = keys.stream()
//                .filter(StringUtils::hasText)
//                .map(k -> ObjectIdentifier.builder().key(k).build())
//                .collect(Collectors.toList());
//        if (identifiers.isEmpty()) {
//            return List.of();
//        }
//
//        Delete delete = Delete.builder()
//                .objects(identifiers)
//                .build();
//        DeleteObjectsRequest batchReq = DeleteObjectsRequest.builder()
//                .bucket(bucketName)
//                .delete(delete)
//                .build();
//
//        try {
//            DeleteObjectsResponse resp = s3Client.deleteObjects(batchReq);
//            // 返回实际删除的 key 列表
//            return resp.deleted().stream()
//                    .map(DeletedObject::key)
//                    .collect(Collectors.toList());
//        } catch (MultiObjectDeleteException e) {
//            // 部分失败时，返回成功和失败的 key，可根据 e.getErrors() 进一步处理
//            List<String> succeeded = e.deleted().stream()
//                    .map(DeletedObject::key)
//                    .collect(Collectors.toList());
//            List<String> failed = e.errors().stream()
//                    .map(err -> err.key())
//                    .collect(Collectors.toList());
//            log.error("批量删除部分失败，成功: {}, 失败: {}", succeeded, failed);
//            return succeeded;
//        } catch (S3Exception e) {
//            throw new RuntimeException("批量删除失败: " + e.awsErrorDetails().errorMessage(), e);
//        }
//    }

    /**
     * 删除远程文件并清理空目录结构
     *
     * @param remotePath    远程文件完整路径（需符合 user_xxx 目录结构）
     * @param currentUserId 当前操作的用户ID（用于权限校验）
     * @throws SecurityException      当用户ID与文件所属用户不匹配时抛出
     * @throws IllegalArgumentException 当路径格式错误或不在允许范围内时抛出
     * @throws RuntimeException       当SFTP操作失败时抛出
     */
    public void deleteFile(String remotePath, Integer currentUserId) {
        validateParameters(remotePath, currentUserId);
        validatePathSecurity(remotePath);

        Integer fileUserId = extractUserIdFromPath(remotePath);
        validateUserPermission(currentUserId, fileUserId);

        executeSftpDeleteOperation(remotePath);
    }

    /**
     * 执行SFTP删除操作（含空目录清理）
     *
     * @param remotePath 待删除文件的完整路径
     */
    private void executeSftpDeleteOperation(String remotePath) {
        Session session = null;
        ChannelSftp channel = null;
        try {
            // 从池中获取 SFTP 连接
            channel = sftpPool.borrowObject();
            deleteTargetFile(channel, remotePath);
            cleanEmptyDirectories(channel, remotePath);
        } catch (JSchException | SftpException e) {
            handleSftpException("文件删除失败", remotePath, e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            // 归还连接
            if (channel != null) {
                try { sftpPool.returnObject(channel); } catch (Exception ignored) {}
            }
        }
    }

    /**
     * 删除目标文件
     */
    private void deleteTargetFile(ChannelSftp channel, String remotePath) throws SftpException {
        try {
            channel.rm(remotePath);
            logger.info("成功删除文件: {}", remotePath);
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                logger.warn("尝试删除不存在的文件: {}", remotePath);
                throw new IllegalArgumentException("文件不存在: " + remotePath, e);
            }
            throw e;
        }
    }

    /**
     * 参数基础校验
     */
    private void validateParameters(String remotePath, Integer currentUserId) {
        if (remotePath == null || remotePath.trim().isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        if (currentUserId == null || currentUserId < 0) {
            throw new IllegalArgumentException("用户ID格式错误");
        }
    }

    /**
     * 路径安全性校验（防止越权访问）
     */
    private void validatePathSecurity(String remotePath) {
        Path base = Paths.get(basePath).toAbsolutePath().normalize();
        Path target = Paths.get(remotePath).toAbsolutePath().normalize();

        if (!target.startsWith(base)) {
            logger.error("非法路径访问尝试: {}", remotePath);
            throw new SecurityException("路径越权访问: " + remotePath);
        }
    }

    /**
     * 用户权限校验
     */
    private void validateUserPermission(Integer currentUserId, Integer fileUserId) {
        if (!currentUserId.equals(fileUserId)) {
            logger.warn("用户{}尝试越权删除用户{}的文件", currentUserId, fileUserId);
            throw new SecurityException("用户无操作权限");
        }
    }

    /**
     * 清理空目录结构（向上递归至基础目录）
     */
    private void cleanEmptyDirectories(ChannelSftp channel, String remotePath) throws SftpException {
        Path path = Paths.get(remotePath).getParent();
        Path baseDir = Paths.get(basePath).toAbsolutePath().normalize();

        while (path != null && !path.equals(baseDir)) {
            String currentDir = path.toString().replace("\\", "/");
            if (isDirectoryEmpty(channel, currentDir)) {
                deleteEmptyDirectory(channel, currentDir);
                path = path.getParent();
            } else {
                break;
            }
        }
    }

    /**
     * 判断目录是否为空
     */
    private boolean isDirectoryEmpty(ChannelSftp channel, String dirPath) throws SftpException {
        try {
            Vector<ChannelSftp.LsEntry> entries = channel.ls(dirPath);
            return entries.stream()
                    .map(ChannelSftp.LsEntry::getFilename)
                    .noneMatch(name -> !name.equals(".") && !name.equals(".."));
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                return true;
            }
            throw e;
        }
    }

    /**
     * 删除空目录
     */
    private void deleteEmptyDirectory(ChannelSftp channel, String dirPath) throws SftpException {
        try {
            channel.rmdir(dirPath);
            logger.debug("已清理空目录: {}", dirPath);
        } catch (SftpException e) {
            logger.error("目录删除失败: {}", dirPath, e);
            throw new SftpException(e.id, "目录删除失败: " + dirPath);
        }
    }

    /**
     * 从路径中提取用户ID
     *
     * @throws IllegalArgumentException 当路径不符合约定格式时抛出
     */
    private Integer extractUserIdFromPath(String remotePath) {
        String[] segments = remotePath.split("/");
        for (String segment : segments) {
            if (segment.startsWith("user_")) {
                try {
                    return Integer.parseInt(segment.substring(5));
                } catch (NumberFormatException e) {
                    throw new IllegalArgumentException("用户ID格式错误: " + segment, e);
                }
            }
        }
        throw new IllegalArgumentException("路径缺少用户ID标识: " + remotePath);
    }

    /**
     * 统一异常处理
     */
    private void handleSftpException(String message, String remotePath, Exception e) {
        String errorDetail = String.format("%s [路径: %s] -> %s",
                message, remotePath, e.getMessage());
        logger.error(errorDetail, e);
        throw new RuntimeException(errorDetail, e);
    }

    /**
     * 资源关闭方法
     */
    private void closeResources(ChannelSftp channel, Session session) {
        try {
            if (channel != null && channel.isConnected()) {
                channel.disconnect();
            }
        } catch (Exception e) {
            logger.warn("SFTP通道关闭异常: {}", e.getMessage());
        }
        try {
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        } catch (Exception e) {
            logger.warn("会话关闭异常: {}", e.getMessage());
        }
    }
}